package rs.fon.is.music.parser;

import java.io.IOException;
import java.net.URI;
import java.util.GregorianCalendar;
import java.util.LinkedList;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import rs.fon.is.music.domain.MusicAlbum;
import rs.fon.is.music.domain.MusicGroup;
import rs.fon.is.music.domain.MusicRecording;
import rs.fon.is.music.parser.exceptions.MusicGroupNotFoundException;
import rs.fon.is.music.parser.threadpool.CustomThreadPoolExecutor;
import rs.fon.is.music.persistence.DataModelManager;
import rs.fon.is.music.util.URIGenerator;

public class MusicGroupParser implements DomainClassParser<MusicGroup> {

	@Override
	public MusicGroup parse(String url) {

		Document doc;
		
		try {
			doc = Jsoup.connect(url).get();

			Elements groupElements = doc.getElementsByAttributeValue("itemtype","http://schema.org/MusicGroup");

			if (groupElements.size() > 0) {

				MusicGroup musicGroup = new MusicGroup();

				Element groupElement = groupElements.first();

				// URI
				musicGroup.setUri(URIGenerator.generate(musicGroup));
				
				// URL
				musicGroup.setUrl(new URI(url));

				// name
				if (groupElement.getElementsByAttributeValue("itemprop", "name").size() > 0)
					musicGroup.setName(groupElement.getElementsByAttributeValue("itemprop", "name").first().text());

				// image
				if (groupElement.getElementsByAttributeValue("itemprop", "image").size() > 0)
					musicGroup.setImage(new URI(groupElement.getElementsByAttributeValue("itemprop", "image").first().attr("src")));

				// foundingDate
				Elements date = groupElement.getElementsByAttributeValue("itemprop", "foundingDate");
				if (date.size() > 0) {
					int year = Integer.parseInt(date.first().text());
					musicGroup.setFoundingDate(new GregorianCalendar(year, 0, 1).getTime());
				}

				// interactionCount
				if (groupElement.getElementsByAttributeValue("itemprop","interactionCount").size() > 0)
					musicGroup.setInteractionCount(groupElement.getElementsByAttributeValue("itemprop","interactionCount").first().attr("content"));
				
				// albums
				LinkedList<MusicAlbum> albums = parseAlbums(url);
				musicGroup.setAlbums(albums);

				for (MusicAlbum album : albums) {
						// group - tracks
						musicGroup.addTracks(album.getTracks());
						// album - byArtist
						album.setArtist(musicGroup);
						for (MusicRecording recording : album.getTracks()) {
							// recording - byArtist
							recording.setArtist(musicGroup);
							// recording - inAlbum
							recording.setAlbum(album);
						}
				}

				// events
				EventParser ep = new EventParser();
				musicGroup.setEvents(ep.parseEvents(url));
				
				DataModelManager.getInstance().save(musicGroup);
				
				return musicGroup;

			} else
				throw new MusicGroupNotFoundException("schema:MusicGroup instance is not found on this URL.");
		
		} catch (IOException e) {
			return parse(url);
		
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}
	
	public LinkedList<MusicAlbum> parseAlbums(String url){

		Document doc;
		
		try {
			doc = Jsoup.connect(url + "/+albums").get();
			
			LinkedList<MusicAlbum> albums = new LinkedList<>();
			LinkedList<MusicAlbumParser> albumParsers = new LinkedList<>();
			
			Elements paginationElements = doc.getElementsByClass("pagination");
			
			int pages = 1;
			
			if(paginationElements.size() > 0){
				pages = Integer.parseInt(paginationElements.first().getElementsByClass("lastpage").first().text());
			}
			
			BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable>(50);
			CustomThreadPoolExecutor executor = new CustomThreadPoolExecutor(10, 20, 5000, TimeUnit.MILLISECONDS, blockingQueue);
			executor.prestartAllCoreThreads();
			
			// UMESTO 1 IDE PAGES
			for (int i = 0; i < 1; i++) {
				parseAlbumsOnPage(i, doc, url, executor, albumParsers);
			}
			
			while(true){
				if(executor.getActiveCount()==0){
					executor.shutdown();
					break;
				}
			}
			
			for (MusicAlbumParser albumParser : albumParsers) {
				MusicAlbum album = albumParser.getAlbum();
				if(album != null)
					albums.add(album);
			}

			return albums;
			
		} catch (IOException e) {
			return parseAlbums(url);
			
		} catch (Exception e) {
			return null;
		}
	}

	public void parseAlbumsOnPage(int pageNum, Document doc, String url, CustomThreadPoolExecutor executor, LinkedList<MusicAlbumParser> albumParsers){
		
		try {
			if(pageNum>0){
				int newPage = pageNum+1;
				doc = Jsoup.connect(url + "/+albums?order=reach&page=" + newPage).get();
			}
			
			Elements albumElements = doc.getElementsByAttributeValue("itemtype","http://schema.org/MusicAlbum");
			
			String noHttp = url.substring(7, url.length());
			String domain = noHttp.substring(0, noHttp.indexOf('/'));

			for (Element albumElement : albumElements) {
					// URL
					String albumURL = albumElement.getElementsByAttributeValue("itemprop", "url").first().attr("href");

					if (!albumURL.startsWith("http://" + domain))
						albumURL = "http://" + domain + albumURL;
					
					int numTracks = 0;
					// numTracks
					if (albumElement.getElementsByAttributeValue("itemprop", "numTracks").size() > 0)
						numTracks = Integer.parseInt(albumElement.getElementsByAttributeValue("itemprop", "numTracks").first().text());
					
					MusicAlbumParser albumParser = new MusicAlbumParser(albumURL, numTracks);
					albumParsers.add(albumParser);
					executor.execute(albumParser);
						
					//break; //da prikupi samo jedan album	
			}
		} catch (NumberFormatException e) {
	
		} catch (IOException e) {
			parseAlbumsOnPage(pageNum, doc, url, executor, albumParsers);
		}
	}
}
